﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using RFServer.Functions;
using RFServer.ServiceWCF;
using Dokan;

namespace RFServer.Drive
{
    class RFS : DokanOperations
    {
        private const string ROOT = "\\";

        /// <summary>
        /// Функция отрабатывает, когда пользователь хочет просмотреть список
        /// файлов по пути filename
        /// </summary>
        /// <param name="filename">Путь (набор тэгов tag1\tag2\tag3)</param>
        /// <param name="files">Набор фалов типа FileInformation для отображения пользователю</param>
        /// <param name="info">?</param>
        /// <returns>DOKAN_SUCCESS в случае успеха</returns>

        public int FindFiles(string filename, ArrayList files, DokanFileInfo info)
        {
            string[] items = null;

            /* Если службы выключены, то функция не поддерживается */

            if (!ServiceControl.Status.IsRunning())
                return DokanNet.DOKAN_SUCCESS;

            /* Соединяемся по NamedPipe с сервером */

            try
            {
                ILocalService service = LocalService.Connect();

                if (service == null)
                    throw new Exception("Failed to create NamedPipe channel");

                /* Выполняем поиск файлов по их тэгам */

                if (filename == ROOT)
                    items = service.Tags(null);
                else
                {
                    List<string> tags = new List<string>(filename.Split('\\'));
                    tags.Remove("");
                    items = service.Select(tags.ToArray());
                }

                /* Составляем набор файлов */

                foreach (string item in items)
                {
                    /* Получаем информацию об объекте */

                    string[] parameters = item.Split('?');

                    /* Определяем тип объекта */

                    string filePathName = parameters[0];
                    int fileType = 0;

                    Int32.TryParse(parameters[1], out fileType);

                    /* Добавляем объект в набор */

                    FileInformation file = new FileInformation();

                    /* Заполняем информацию по умолчанию */

                    switch (fileType)
                    {
                        case 0: file.Attributes = FileAttributes.Normal; break;
                        case 1: file.Attributes = FileAttributes.Normal; break;
                        case 2: file.Attributes = FileAttributes.Directory; break;
                    }

                    file.FileName = filePathName.GetFileName();
                    file.LastAccessTime = DateTime.Now;
                    file.LastWriteTime = DateTime.Now;
                    file.CreationTime = DateTime.Now;

                    /* Если объект реально существует на жестком диске пользователя, то */
                    /* заполняем его характеристики реальными данными                   */

                    switch (fileType)
                    {
                        case 0:

                            /* Файл. Если он реально существует, то заполняем информацию о нем  */
                            /* реальными данными                                                */

                            if (File.Exists(filePathName))
                            {
                                FileInfo f = new FileInfo(filePathName);

                                file.Attributes = f.Attributes;
                                file.CreationTime = f.CreationTime;
                                file.LastAccessTime = f.LastAccessTime;
                                file.LastWriteTime = f.LastWriteTime;
                                file.FileName = filePathName;
                                file.Length = f.Length;
                            }

                            break;

                        case 1:

                            /* Директория. Если она реально существует, то заполняем информацию */
                            /* о ней реальными данными                                          */

                            if (Directory.Exists(filePathName))
                            {
                                DirectoryInfo d = new DirectoryInfo(filePathName);

                                file.Attributes = d.Attributes;
                                file.CreationTime = d.CreationTime;
                                file.LastAccessTime = d.LastAccessTime;
                                file.LastWriteTime = d.LastWriteTime;
                                file.FileName = d.FullName.GetFileName();
                            }

                            break;
                    }

                    files.Add(file);
                }

                /* Отсоединяемся от NamedPipe */

                LocalService.Disconnect(service);

                /* Функция успешно отработала */

                return DokanNet.DOKAN_SUCCESS;
            }
            catch (Exception e)
            {
                Setup.Log("Failed to connect by NamedPipe", e);
                return DokanNet.DOKAN_ERROR;
            }
        }

        public int GetFileInformation(string filename, FileInformation fileinfo, DokanFileInfo info)
        {
            /* Если службы выключены, то функция не поддерживается */

            if (!ServiceControl.Status.IsRunning())
                return DokanNet.DOKAN_SUCCESS;

            // Директория

            if (info.IsDirectory)
            {
                if (Setup.LogAdvanced)
                    Setup.Log("[rfs]: FileInformation for directory " + filename);

                fileinfo.Attributes = FileAttributes.Directory;
                fileinfo.LastAccessTime = DateTime.Now;
                fileinfo.LastWriteTime = DateTime.Now;
                fileinfo.CreationTime = DateTime.Now;

                return 0;
            }

            // Файл

            FileLink link = new FileLink(filename);

            if (link.RealFileName != null)
            {
                if (Setup.LogAdvanced)
                    Setup.Log("[rfs]: FileInformation for existing file " + link.RealFileName);

                FileInfo file = new FileInfo(link.RealFileName);

                fileinfo.Attributes = file.Attributes;
                fileinfo.LastAccessTime = file.LastAccessTime;
                fileinfo.LastWriteTime = file.LastWriteTime;
                fileinfo.CreationTime = file.CreationTime;
                fileinfo.Length = file.Length;
                fileinfo.FileName = file.FullName;
            }
            else
            {
                if (Setup.LogAdvanced)
                    Setup.Log("[rfs]: FileInformation for unknown file " + filename);

                fileinfo.Attributes = FileAttributes.Normal;
                fileinfo.LastAccessTime = DateTime.Now;
                fileinfo.LastWriteTime = DateTime.Now;
                fileinfo.CreationTime = DateTime.Now;
                fileinfo.Length = 0;
            }

            return 0;
        }

        #region Неподдерживаемые функции

        public int Cleanup(string filename, DokanFileInfo info)
        {
            return 0;
        }

        public int CloseFile(string filename, DokanFileInfo info)
        {
            return 0;
        }

        public int CreateDirectory(string filename, DokanFileInfo info)
        {
            return DokanNet.DOKAN_SUCCESS;
        }

        public int CreateFile(string filename, FileAccess access, FileShare share, FileMode mode, FileOptions options, DokanFileInfo info)
        {
            return DokanNet.DOKAN_SUCCESS;
        }

        public int DeleteFile(string filename, DokanFileInfo info)
        {
            return DokanNet.DOKAN_ERROR;
        }

        public int DeleteDirectory(string filename, DokanFileInfo info)
        {
            return DokanNet.DOKAN_SUCCESS;
        }

        public int FlushFileBuffers(string filename, DokanFileInfo info)
        {
            return -1;
        }

        public int LockFile(string filename, long offset, long length, DokanFileInfo info)
        {
            return 0;
        }

        public int MoveFile(string filename, string newname, bool replace,  DokanFileInfo info)
        {
            return DokanNet.DOKAN_SUCCESS;
        }

        public int OpenDirectory(string filename, DokanFileInfo info)
        {
            return 0;
        }

        public int ReadFile(string filename, byte[] buffer, ref uint readBytes, long offset, DokanFileInfo info)
        {
            return DokanNet.DOKAN_SUCCESS;
        }

        public int SetEndOfFile(string filename, long length, DokanFileInfo info)
        {
            return -1;
        }

        public int SetAllocationSize(string filename, long length, DokanFileInfo info)
        {
            return -1;
        }

        public int SetFileAttributes(string filename, FileAttributes attr, DokanFileInfo info)
        {
            return -1;
        }

        public int SetFileTime(string filename, DateTime ctime, DateTime atime, DateTime mtime, DokanFileInfo info)
        {
            return -1;
        }

        public int UnlockFile(string filename, long offset, long length, DokanFileInfo info)
        {
            return 0;
        }

        public int Unmount(DokanFileInfo info)
        {
            return 0;
        }

        public int GetDiskFreeSpace(ref ulong freeBytesAvailable, ref ulong totalBytes, ref ulong totalFreeBytes, DokanFileInfo info)
        {
            freeBytesAvailable = 0;
            totalBytes = 0;
            totalFreeBytes = 0;
            return 0;
        }

        public int WriteFile(string filename, byte[] buffer, ref uint writtenBytes, long offset, DokanFileInfo info)
        {
            return -1;
        }
    
        #endregion
    }
}
